#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "../comm/util.hpp"
#include "../comm/log.hpp"

namespace ns_runner
{
    using namespace ns_util;
    using namespace ns_log;

    class Runner
    {
    public:
        Runner() = default;
        ~Runner() = default;

        /*********************************
         * Run 函数负责运行程序并且判断程序是否正常运行，不保证运行结果的正确与否，这和测试用例有关
         * 参数：file_name -- 文件的原始名称，不带路径；cpu_limit&&mem_limit -- 程序运行的cpu限制和mem限制（kb）
         * 返回值：>0 ：程序被信号终结，返回值为信号序号
         * =0 ：程序正常运行
         * <0 ：内部错误
         *********************************/
        static int Run(const std::string &file_name, int cpu_limit, int mem_limit)
        {
            // 程序运行会默认打开三个流：stdin，stdout，stderr 要用文件记录这三个流的信息
            std::string exe = PathUtil::Exe(file_name);
            std::string input = PathUtil::Stdin(file_name);
            std::string output = PathUtil::Stdout(file_name);
            std::string error = PathUtil::Stderr(file_name);

            umask(0);
            int infd = open(input.c_str(), O_CREAT | O_RDONLY, 0644);
            int outfd = open(output.c_str(), O_CREAT | O_WRONLY, 0644);
            int errfd = open(error.c_str(), O_CREAT | O_WRONLY, 0644);
            if (infd < 0 || outfd < 0 || errfd < 0)
            {
                LOG(ERROR) << "运行时，文件打开失败！" << std::endl;
                return -1; // 文件打开失败
            }

            pid_t pid = fork();
            if (pid < 0)
            {
                close(infd);
                close(outfd);
                close(errfd);
                LOG(ERROR) << "运行时，创建子进程失败" << std::endl;
                return -2; // 表示打开子进程失败
            }
            else if (pid == 0)
            {
                // 重定向标识符
                dup2(infd, 0);
                dup2(outfd, 1);
                dup2(errfd, 2);

                // 设置文件资源限制
                ResourceUtil::SetResourceLimit(cpu_limit, mem_limit);
                // 先找到此程序，再输入调用此程序的命令行参数
                execl(exe.c_str(), exe.c_str(), nullptr);
                LOG(ERROR) << "运行时，进程替换失败！" << std::endl;
                exit(-3);
            }
            else
            {
                close(infd);
                close(outfd);
                close(errfd);

                int status = 0;
                // 如果子进程被异常终止，status会记录终止信号的序号
                waitpid(pid, &status, 0);
                LOG(INFO) << exe << " 运行完毕: " << (status & 0x7f) <<  std::endl;
                return status & 0x7f;
            }
        }
    };
}

